package edu.nmt.cs.cse326.team4.crap;

import java.util.ArrayList;
import java.util.HashMap;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnCameraChangeListener;
import com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener;
import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class ViewMapActivity extends FragmentActivity implements LocationListener, LocationSource, OnCameraChangeListener, OnMarkerClickListener, OnInfoWindowClickListener {
	// TODO: put these global state variables in a more logical place (like a global state class)
	public static ArrayList<Toilet> toilets; // globally accessible list of matching toilets
	public static Context context; // globally accessible context
	
	SharedPreferences prefs;

	/**
	 * Note that this may be null if the Google Play services APK is not available.
	 */
	private static GoogleMap map;
	private static final int DEFAULT_ZOOM = 14; // zoom level of map when it first opens
	private static LatLng center; // center of map

	private ToiletInfoWindowAdapter infoWindowAdapter; // pops up when a marker is clicked?
	private HashMap<Marker, Toilet> markerRef; // map of markers to toilets?
	
	

	// TODO: stop using this hard-coded distance. use user pref instead
	private static int DISTANCE = 5000; // hard-coded search distance
	private static ArrayList<String> amenities;
	
	// some things related to user location
	private OnLocationChangedListener mListener;
	private static LocationManager locationManager;
	private static Location current; // user location (if you can get it)
	// TODO: fix whatever is causing devices not to get user location and quit falling back on cramer
	public static LatLng CRAMER = new LatLng(34.0666, -106.9062);

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_mapview);

		context = this; // set this global state variable first thing
		// get shared prefs
		prefs = getSharedPreferences("crap", 0);
		SettingsActivity.view = "map"; // what is this for?

		// get user location
		setUpLocationManager();
		current = getUserLocation();

		// initialize map
		setUpMapIfNeeded();
		
		//new GetNearbyToilets().execute();
	}

	/** Show nearby toilets on the map. */
	private void update() {
		
		
		
		// initialize marker things
		markerRef = new HashMap<Marker, Toilet>();
		infoWindowAdapter = new ToiletInfoWindowAdapter(markerRef, this);			
		
		// add toilet markers
		for (Toilet toilet : toilets) {
			Marker mk = map.addMarker(new MarkerOptions().title(toilet.getName()).position(toilet.getLocation())
					.icon(BitmapDescriptorFactory.fromResource(R.drawable.toilet_black)));

			markerRef.put(mk, toilet);
		}
		
		// set us up the info windows
		infoWindowAdapter.setHashMap(markerRef);
	}

	@Override
	public void onPause() {
		super.onPause();
		// pause location listener so we don't waste battery
		if (locationManager != null) { locationManager.removeUpdates(this); }
	}

	@Override
	public void onResume() {
		super.onResume();
		setUpMapIfNeeded();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		if (prefs.getString("user", null) != null)
			getMenuInflater().inflate(R.menu.map_view, menu);
		else
			getMenuInflater().inflate(R.menu.unreg_map_view, menu);
		return true;
	}

	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.menu_settings:
			Intent intent = new Intent(this, SettingsActivity.class);
			startActivity(intent);
			finish();
			return true;
		case R.id.listview:
			intent = new Intent(this, ListViewActivity.class);
			intent.putExtra("toilets", toilets);
			startActivity(intent);
			finish();
			return true;
		case R.id.login:
			intent = new Intent(this, LoginActivity.class);
			startActivity(intent);
			finish();
		case R.id.logout:
			prefs.edit().putString("user", null).commit();
			intent = new Intent(this, LoginActivity.class);
			startActivity(intent);
			finish();
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	@Override
	public void onLocationChanged(Location location) {

		if (mListener != null) {
			mListener.onLocationChanged(location);

			// center map on user's current location if (map != null)
			if (map != null) {
				map.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(location.getLatitude(), location.getLongitude())));
			}
		}
	}

	@Override
	public void onProviderDisabled(String provider) {
	}

	@Override
	public void onProviderEnabled(String provider) {
	}

	@Override
	public void onStatusChanged(String provider, int status, Bundle extras) {
	}

	/** Sets up the location manager. */
	private void setUpLocationManager() {
		locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
		if (locationManager != null) {
			boolean gpsIsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
			boolean networkIsEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
			try {
				if (gpsIsEnabled) {
					locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000L, 10F, this);
				} else if (networkIsEnabled) {
					locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 5000L, 10F, this);
				} else {
					// TODO: show an error dialog that GPS is disabled...
				}
			} catch (SecurityException e) {
				Log.e("setUpLocationManager", e.getMessage());
			}
		} else {
			// TODO: show some generic error dialog because something must have
			// gone wrong with location manager.
		}
	}

	/**
	 * Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly installed) and the map has not already been instantiated..
	 * This will ensure that we only ever call {@link #setUpMap()} once when {@link #mMap} is not null.
	 * <p>
	 * If it isn't installed {@link SupportMapFragment} (and {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
	 * install/update the Google Play services APK on their device.
	 * <p>
	 * A user can return to this Activity after following the prompt and correctly installing/updating/enabling the Google Play services. Since the Activity may
	 * not have been completely destroyed during this process (it is likely that it would only be stopped or paused), {@link #onCreate(Bundle)} may not be
	 * called again so we should call this method in {@link #onResume()} to guarantee that it will be called.
	 */
	private void setUpMapIfNeeded() {
		// Do a null check to confirm that we have not already instantiated the
		// map.
		if (map == null) {
			// Try to obtain the map from the MapFragment.
			map = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
			// Check if we were successful in obtaining the map.
		}
//		if (map != null) {
			setUpMap();
			map.setLocationSource(this);
//		}
	}

	/** Sets up a map centered on user's current location. */
	private void setUpMap() {
		map.setMyLocationEnabled(true);
		map.setOnInfoWindowClickListener(this);
		map.setOnCameraChangeListener(this);
		map.setOnMarkerClickListener(this);
		map.setInfoWindowAdapter(infoWindowAdapter);
		if (current != null)
			map.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(current.getLatitude(), current.getLongitude()), DEFAULT_ZOOM));
		else
			map.animateCamera(CameraUpdateFactory.newLatLngZoom(CRAMER, DEFAULT_ZOOM));
	}

	/**
	 * Get the user's current location.
	 * 
	 * @return {@link Location}
	 */
	public static Location getUserLocation() {
		try {
			return locationManager.getLastKnownLocation(locationManager.getBestProvider(new Criteria(), false));
		} catch (SecurityException e) {
			Log.e("getUserLocation", e.getMessage());
		}
		return null;
	}

	@Override
	public void activate(OnLocationChangedListener listener) {
		mListener = listener;
	}

	@Override
	public void deactivate() {
		mListener = null;
	}

	/**
	 * Updates map whenever camera is moved.
	 */
	@Override
	public void onCameraChange(CameraPosition position) {
		if (current == null) {
			map.clear();
			center = position.target;
			
			new GetNearbyToilets().execute();
		}
	}

	@Override
	/**
	 * activated when a Marker is clicked by the user.
	 * Displays an infoWindow to the user
	 */
	public boolean onMarkerClick(Marker marker) {
		marker.showInfoWindow();
		return true;
	}

	@Override
	/**
	 * activated when the infoWindow of a Marker is clicked by the user.
	 * switched activity to a ViewDetails
	 */
	public void onInfoWindowClick(Marker marker) {
		Intent intent = new Intent(this, ViewDetailsActivity.class);
		intent.putExtra("toilet", getToilet(marker.getTitle()));
		startActivity(intent);
		finish();
	}

	/**
	 * Gets toilet by name.
	 * @param name
	 * @return
	 */
	public Toilet getToilet(String name) {
		for (Toilet t : toilets)
			if (t.getName().equals(name))
				return t;
		return null;
	}
	
	/**
	 * Background task to fetch toilets from the server. Called in onCameraChange()
	 * @author james
	 *
	 */
	class GetNearbyToilets extends AsyncTask<String, String, String> {
		
		/**
		 * Go get them toilets!
		 */
		@Override
		protected String doInBackground(String... params) {
			amenities = SettingsActivity.getAmenities();
			DISTANCE = SettingsActivity.getDistance();
			toilets = DBAdapter.getNearbyToilets(center, DISTANCE, amenities);
			return null;
		}
		
		/**
		 * After getting toilets, update map.
		 */
		protected void onPostExecute(String result) {
			update();
		}
		
	}

}

